﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using JQuery.WebKit.Common.Utilities;

namespace JQuery.WebKit.WebCommon.Compression
{
    /// <summary>
    /// Find scripts and change the src to the ScriptCompressorHandler.
    /// </summary>
    public class CompressorModule : IHttpModule
    {
        #region IHttpModule Members

        void IHttpModule.Dispose()
        {
            // Nothing to dispose; 
        }

        void IHttpModule.Init(HttpApplication context)
        {
            //context.PostRequestHandlerExecute += new EventHandler(context_BeginRequest);
            context.PostRequestHandlerExecute += new EventHandler(context_PostRequestHandlerExecute);
        }

        void context_PostRequestHandlerExecute(object sender, EventArgs e)
        {
            HttpApplication app = sender as HttpApplication;
            if (app.Context.CurrentHandler is Page && !app.Request.RawUrl.Contains("serviceframe"))
            {
                if (!app.Context.Request.Url.Scheme.Contains("https"))
                {
                    app.Response.Filter = new WebResourceFilter(app.Response.Filter);
                }
            }
        }

        #endregion
       
        #region Stream filter
        private class WebResourceFilter : Stream
        {

            public WebResourceFilter(Stream sink)
            {
                _sink = sink;
            }

            private Stream _sink;

            #region Properites

            public override bool CanRead
            {
                get { return true; }
            }

            public override bool CanSeek
            {
                get { return true; }
            }

            public override bool CanWrite
            {
                get { return true; }
            }

            public override void Flush()
            {
                _sink.Flush();
            }

            public override long Length
            {
                get { return 0; }
            }

            private long _position;
            public override long Position
            {
                get { return _position; }
                set { _position = value; }
            }

            #endregion

            #region Methods

            public override int Read(byte[] buffer, int offset, int count)
            {
                return _sink.Read(buffer, offset, count);
            }

            public override long Seek(long offset, SeekOrigin origin)
            {
                return _sink.Seek(offset, origin);
            }

            public override void SetLength(long value)
            {
                _sink.SetLength(value);
            }

            public override void Close()
            {
                _sink.Close();
            }

            public override void Write(byte[] buffer, int offset, int count)
            {
                byte[] data = new byte[count];
                Buffer.BlockCopy(buffer, offset, data, 0, count);
                string html = System.Text.Encoding.Default.GetString(buffer);
                List<string> cssList = new List<string>();
                List<string> jsList = new List<string>();

                Regex regex = new Regex(@"(\r\n)?<(script|link).+?(src|href)=\""(.+?(\.css|\.js|.+?)[^\""]*).+?(</script>|</link>|/>)", RegexOptions.IgnoreCase);
                //Regex regex = new Regex(@"<(script|link).+?(src|href)=\""(.+?(\.css|\.js)[^\""]*).+?(</script>|</link>|/>)", RegexOptions.IgnoreCase);
                string relative = null;
                foreach (Match match in regex.Matches(html))
                {
                    relative = HttpUtility.UrlEncode(match.Groups[4].Value);
                    switch (match.Groups[2].Value)
                    {
                        case "script":
                            if (!jsList.Contains(relative))
                                jsList.Add(relative);
                            html = html.Replace(match.Value, string.Empty);
                            break;
                        case "link":
                            if (!cssList.Contains(relative))
                                cssList.Add(relative);
                            html = html.Replace(match.Value, string.Empty);
                            break;
                    }
                }

                int headIndex = html.IndexOf("<head>");
                if (headIndex < 0)
                {
                    _sink.Write(buffer, offset , count);
                    return;
                }
                headIndex += 6;
                if (jsList.Count > 0)
                {
                    string path = string.Join(",", jsList.ToArray());
                    string cachePath = SetUrlCacheKey(path);
                    string script = string.Format("\r\n<script type=\"text/javascript\" src=\"scriptRef.axd?d={0}\"></script>", cachePath);
                    html = html.Insert(headIndex, script);
                    headIndex += script.Length;
                }
                if (cssList.Count > 0)
                {
                    string path = string.Join(",", cssList.ToArray());
                    string cachePath = SetUrlCacheKey(path);
                    string script = string.Format("\r\n<link rel=\"stylesheet\" type=\"text/css\" href=\"cssRef.axd?d={0}\" />\r\n", cachePath);
                    html = html.Insert(headIndex, script);
                }
                while (html.IndexOf("  ") > 0)
                {
                    html = html.Replace("  ", "");
                }
                byte[] outdata = System.Text.Encoding.Default.GetBytes(html);
                _sink.Write(outdata, 0, outdata.GetLength(0));
            }

            private string SetUrlCacheKey(string url)
            {
                string hashCode = HttpUtility.UrlEncode(Rnd.GetRandomAlphaNumericString(10));
                if (HttpContext.Current.Cache[hashCode] == null)
                {
                    HttpContext.Current.Cache.Insert(hashCode, url, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 20, 0));
                }
                return hashCode;
            }
            #endregion
        }
        #endregion
    }
}
